#include "tan_floatobject.h"
#include "tan_nbobject.h"
#include "tan_alloc.h"

static const TAN_INT8* tan_float_type = "float";

static TanObject* float_copy(TanObject* other)
{
	TanFloatObject* var_object = 0;
	if (other)
	{
		if (strcmp(other->object_type, tan_float_type) == 0)
		{
			var_object = Tan_Float(Tan_Float_cast(other)->float_var);
			return Tan_Object_cast(var_object);
		}
		else if (strcmp(other->object_type, "number") == 0)
		{
			var_object = Tan_Float((TAN_FLOAT)(Tan_Number_cast(other)->number));
			return Tan_Object_cast(var_object);
		}
	}
	else
	{
		var_object = Tan_Float(0);
		return Tan_Object_cast(var_object);
	}
}

static void float_delete(TanObject* oneself)
{
	free(Tan_Float_cast(oneself));
}

void float_clear(TanObject* oneself)
{
	Tan_Float_cast(oneself)->float_var = 0;
}

void float_repr(TanObject* oneself)
{
	printf("%lf", Tan_Float_cast(oneself)->float_var);
}

Tan_bool float_compare(TanObject* oneself, TanObject* other, TanCompareType type)
{
	TanFloatObject* self_float = Tan_Float_cast(oneself);

	if (other)
	{
		if (strcmp(other->object_type, tan_float_type) == 0)
		{
			TanFloatObject* other_float = Tan_Float_cast(other);
			if (type == Tan_compare_equal && self_float->float_var == other_float->float_var)
				return Tan_true;
			else if (type == Tan_compare_noequal && self_float->float_var != other_float->float_var)
				return Tan_true;
			else if (type == Tan_compare_greaterThan && self_float->float_var > other_float->float_var)
				return Tan_true;
			else if (type == Tan_compare_lessThan && self_float->float_var < other_float->float_var)
				return Tan_true;
			else if (type == Tan_compare_grea_equal && self_float->float_var >= other_float->float_var)
				return Tan_true;
			else if (type == Tan_compare_less_equal && self_float->float_var <= other_float->float_var)
				return Tan_true;
			else
			{
				Tan_RepError(false, "'float' type does not support this operator.", other);
			}
		}
		else if (strcmp(other->object_type, "number") == 0)
		{
			TanNumberObject* other_nb = Tan_Number_cast(other);
			if (type == Tan_compare_equal && self_float->float_var == (TAN_FLOAT)(other_nb->number))
				return Tan_true;
			else if (type == Tan_compare_noequal && self_float->float_var != (TAN_FLOAT)(other_nb->number))
				return Tan_true;
			else if (type == Tan_compare_greaterThan && self_float->float_var > (TAN_FLOAT)(other_nb->number))
				return Tan_true;
			else if (type == Tan_compare_lessThan && self_float->float_var < (TAN_FLOAT)(other_nb->number))
				return Tan_true;
			else if (type == Tan_compare_grea_equal && self_float->float_var >= (TAN_FLOAT)(other_nb->number))
				return Tan_true;
			else if (type == Tan_compare_less_equal && self_float->float_var <= (TAN_FLOAT)(other_nb->number))
				return Tan_true;
			else
			{
				Tan_RepError(false, "'float' type does not support this operator.", other);
			}
		}
		else
		{
			Tan_RepError(false, 
				"The 'float' type does not support this operator operation with variables of types other than 'float' and 'number'.",
				other);
		}
	}
	return Tan_false;
}

static void float_assign(TanObject** oneself, TanObject* other, TanAssignType type)
{
	TanFloatObject* self_float = Tan_Float_cast(*oneself);
	if (strcmp(other->object_type, tan_float_type) == 0)
	{
		TanFloatObject* other_float = Tan_Float_cast(other);
		if (type == Tan_assign_equal)
			self_float->float_var = other_float->float_var;
		else if (type == Tan_assign_sum_equal)
			self_float->float_var += other_float->float_var;
		else if (type == Tan_assign_dif_equal)
			self_float->float_var -= other_float->float_var;
		else if (type == Tan_assign_mul_equal)
			self_float->float_var *= other_float->float_var;
		else if (type == Tan_assign_div_equal)
			self_float->float_var /= other_float->float_var;
		else if (type == Tan_assign_sum_sum)
			++self_float->float_var;
		else if (type == Tan_assign_dif_dif)
			--self_float->float_var;
		else
		{
			Tan_RepError(false, "'float' type does not support this operator.", other);
		}
	}
	else if (strcmp(other->object_type, "number") == 0)
	{
		TanNumberObject* other_nb = Tan_Number_cast(other);
		if (type == Tan_assign_equal)
			self_float->float_var = (TAN_FLOAT)(other_nb->number);
		else if (type == Tan_assign_sum_equal)
			self_float->float_var += (TAN_FLOAT)(other_nb->number);
		else if (type == Tan_assign_dif_equal)
			self_float->float_var -= (TAN_FLOAT)(other_nb->number);
		else if (type == Tan_assign_mul_equal)
			self_float->float_var *= (TAN_FLOAT)(other_nb->number);
		else if (type == Tan_assign_div_equal)
			self_float->float_var /= (TAN_FLOAT)(other_nb->number);
		else if (type == Tan_assign_sum_sum)
			++self_float->float_var;
		else if (type == Tan_assign_dif_dif)
			--self_float->float_var;
	}
	else
	{
		Tan_RepError(false,
			"The 'float' type does not support this operator operation with variables of types other than 'float' and 'number'.",
			other);
	}
}

static TanObject* float_operat(TanObject* oneself, TanObject* other, TanOperationType type)
{
	TanFloatObject* self_float = Tan_Float_cast(oneself);

	if (strcmp(other->object_type, tan_float_type) == 0)
	{
		TanFloatObject* other_float = Tan_Float_cast(other);
		TanFloatObject* rt_object = Tan_Float(0);
		if (type == Tan_operation_sum)
			rt_object->float_var = self_float->float_var + other_float->float_var;
		else if (type == Tan_operation_dif)
			rt_object->float_var = self_float->float_var - other_float->float_var;
		else if (type == Tan_operation_mul)
			rt_object->float_var = self_float->float_var * other_float->float_var;
		else if (type == Tan_operation_div)
			rt_object->float_var = self_float->float_var / other_float->float_var;
		else
		{
			Tan_RepError(false, "'float' type does not support this operator.", other);
		}
		return rt_object;
	}
	else if (strcmp(other->object_type, "number") == 0)
	{
		TanNumberObject* other_nb = Tan_Number_cast(other);
		TanFloatObject* rt_object = Tan_Float(0);
		if (type == Tan_operation_sum)
			rt_object->float_var = self_float->float_var + other_nb->number;
		else if (type == Tan_operation_dif)
			rt_object->float_var = self_float->float_var - other_nb->number;
		else if (type == Tan_operation_mul)
			rt_object->float_var = self_float->float_var * other_nb->number;
		else if (type == Tan_operation_div)
			rt_object->float_var = self_float->float_var / other_nb->number;
		else
		{
			Tan_RepError(false, "'float' type does not support this operator.", other);
		}
		return rt_object;
	}
	else
	{
		Tan_RepError(false,
			"The 'float' type does not support this operator operation with variables of types other than 'float' and 'number'.",
			other);
	}
	return 0;
}

static TanObjectType float_object_Type =
{
	float_copy,		// type_copy
	float_delete,	// type_delete
	float_clear,	// type_clear
	float_repr,		// var_repr
	float_compare,	// var_compare
	float_assign,	// var_assign
	float_operat,	// var_operation
	0,
	0				// next
};

TAN_INTERFACE_FUNC(TanObject*) Tan_Float_fromString(const TAN_INT8* str)
{
	TanFloatObject* var_object = Tan_AllocObject_Memset(sizeof(TanFloatObject));
	if (str)
	{
		TAN_FLOAT _float = 0;
		char* isOk = 0;

		_float = strtod(str, &isOk);
		if (!isOk)
		{
			var_object->float_var = _float;
		}
		else
		{
			Tan_RepError(false, "The value is not of float type.", 0);
			return Tan_Object_cast(var_object);
		}
	}

	Tan_Object_cast(var_object)->object_type = "float";
	Tan_Object_cast(var_object)->object_set = &float_object_Type;
	Tan_Var_cast(var_object)->var_size = sizeof(TAN_FLOAT);
	Tan_Var_cast(var_object)->var_req = 1;

	return Tan_Object_cast(var_object);
}

TanObject* Tan_Float(const TAN_FLOAT _float)
{
	TanFloatObject* var_object = Tan_AllocObject_Memset(sizeof(TanFloatObject));

	var_object->float_var = _float;

	Tan_Object_cast(var_object)->object_type = "float";
	Tan_Object_cast(var_object)->object_set = &float_object_Type;
	Tan_Var_cast(var_object)->var_size = sizeof(TAN_FLOAT);
	Tan_Var_cast(var_object)->var_req = 1;

	return var_object;
}
